/* ========================================================================= */
/* ------------------------------------------------------------------------- */
/*!
  \file			CpgLibrary.h
  \date			Nov 2012
  \author		TNick

  \brief		Contains the definition for CpgLibrary class


*//*


 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Please read COPYING and README files in root folder
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
/* ------------------------------------------------------------------------- */
/* ========================================================================= */
#ifndef __CPGLIBRARY_INC__
#define __CPGLIBRARY_INC__
//
//
//
//
/*  INCLUDES    ------------------------------------------------------------ */

#include	<QObject>
#include    <QUndoGroup>

#include	<memtrack.h>
#include	<version3d.h>

#include	<cpg/cpg_global.h>
#include	<cpg/control/pgguidermanager.h>

/*  INCLUDES    ============================================================ */
//
//
//
//
/*  CLASS    --------------------------------------------------------------- */

namespace	BBlocks		{

class	UserMsg;

}

namespace cpg	{

class	CpgDoc;
class	PgSpace;
class	PgLayer;
class	IDrawFile;

/**
*	@brief	The library management class (one instance per application)
*/
class CPGSHARED_EXPORT CpgLibrary		:
		public QObject, public GuiderManager	{
	Q_OBJECT
	BBM_TRACK( CpgLibrary );

	//
	//
	//
	//
	/*  DEFINITIONS    ----------------------------------------------------- */

	friend class CpgDoc;
	friend class PgSpace;
	friend class PgLayer;

#	define	FILETAGSDIR		"file-tags"

	/*  DEFINITIONS    ===================================================== */
	//
	//
	//
	//
	/*  DATA    ------------------------------------------------------------ */

private:

	/**
	*	@brief	list of documents associated with this instance
	*/
	QList<CpgDoc *>		lst_doc_;


	/**
	*	@brief	current document
	*/
	CpgDoc *				crt_doc_;


	/**
	*	@brief	next available id for documents
	*/
	int					id_doc_;


	/**
	*	@brief	exit sequence initiated
	*/
	bool				b_closing_;


	/**
	*	@brief	temporary directory for this instance
	*
	*	This path is guaranteed to end in a slash (/).
	*/
	QString				s_tmp_;


	/**
	*	@brief	undo-redo for each document
	*/
	QUndoGroup			ug_;




	/**
	*	@brief	the one and only instance
	*/
	static CpgLibrary *		uniq_;




	/*  DATA    ============================================================ */
	//
	//
	//
	//
	/*  FUNCTIONS    ------------------------------------------------------- */

public:


	/**
	*	@brief	constructor;
	*/
	CpgLibrary			( void );


	/**
	*	@brief	destructor;
	*/
	~CpgLibrary			( void );


	/**
	*	@brief	the one and only instance
	*/
	static CpgLibrary *		unique						( void )
	{ return uniq_; }



	/**
	*	@brief	marks the instance as being in closing sequence
	*/
	static bool					startClosing		( BBlocks::UserMsg & um );


	/**
	*	@brief	get closing status
	*/
	static inline bool			isClosing			( void )
	{ return uniq_->b_closing_; }


	/**
	*	@brief	get temporary folder for this instance
	*/
	static inline QString		tempDir				( void )
	{ return uniq_->s_tmp_; }


	/**
	*	@brief	get temporary folder for file tags
	*/
	static inline QString		fileTagsTempDir		( void )
	{ return uniq_->s_tmp_ + FILETAGSDIR "/"; }



	/**
	*	@brief	undo-redo for each document
	*
	*	For an comprehensive discussion about Undo/Redo functionality see
	*	\ref undo "dedicated page".
	*/
	static inline QUndoGroup *	undoGroup			( void )
	{ return &uniq_->ug_; }


	/**
	*	@brief	active widget of the application
	*/
	static QWidget *			activeWindow			( void );


	/* ******************************************************************* */
	/** @name 	Documents related methods                                  */
	/* ******************************************************************* */
	///@{


protected:


	/**
	*	@brief	used by documents (CpgDoc) to get appended into internal chain
	*/
	static void			addDocument			( CpgDoc * itm );


	/**
	*	@brief	removes a document from the internal chain
	*/
	static void			remDocument			( CpgDoc * itm );


	/**
	*	@brief	next available id for documents
	*/
	static inline int	nextDocId			( void )
	{ return uniq_->id_doc_++; }


public:


	/**
	*	@brief	creates a new document and sets it as the current document
	*
	*	@param	s_path	where to save new document; if empty the document is not saved
	*	@param	um		error corelation
	*	@param	b_vis	make the CpgDoc visible or not (silent load)
	*/
	static CpgDoc *			createNew			(
			const QString &			s_path,
			BBlocks::UserMsg &		um,
			bool					b_vis = true
			);


	/**
	*	@brief	creates a new document, loads the file and sets it as the current document
	*
	*	The \b b_vis argument controls wether the file is advertised
	*	(CpgLibrary::setCrtDoc() is used to make it current and docEmerging()
	*	signal is emited). As a result the document will be invisible to
	*	other parties like the GUI if \b b_vis is false.
	*
	*	@param	s_file	where is the document located
	*	@param	um		error corelation
	*	@param	b_vis	make the CpgDoc visible or not (silent load)
	*/
	static CpgDoc *			openExisting		(
			const QString &			s_file,
			BBlocks::UserMsg &		um,
			bool					b_vis = true
			);


	/**
	*	@brief	return the list of documents associated with this instance
	*/
	static inline QList<CpgDoc*>	documents		( void )
	{ return uniq_->lst_doc_; }


	/**
	*	@brief	return the number of documents
	*/
	static inline int			docCount	( void )
	{ return uniq_->lst_doc_.count(); }


	/**
	*	@brief	return the document at given index
	*/
	static inline CpgDoc *		document		( int i )
	{
		if ( ( i < 0 ) || ( i > uniq_->lst_doc_.count() ) )
			return NULL;
		return uniq_->lst_doc_.at( i );
	}


	/**
	*	@brief	get the document that has this name
	*/
	static CpgDoc *				docByName		(
			const QString &			s_name
			);


	/**
	*	@brief	get the document that is associated with this file
	*/
	static CpgDoc *				docByFile		(
			const QString &			s_name
			);


	/**
	*	@brief	get the document that is associated with this id
	*/
	static CpgDoc *				docById		(
			int						id
			);


	/**
	*	@brief	set current document
	*/
	static void					setCrtDoc			( CpgDoc * new_val );


	/**
	*	@brief	set current document
	*/
	static void					setCrtDocNS			( CpgDoc * new_val );


	/**
	*	@brief	get current document
	*/
	static inline CpgDoc *		crtDoc				( void )
	{ return uniq_->crt_doc_; }


	/**
	*	@brief	get a document
	*
	*	If document is non-NULL, it is simply returned.
	*	Else If there is a current document, it is returned.
	*	Else NULL is returned and an error message is added.
	*/
	static CpgDoc *				normaliseDoc		(
			BBlocks::UserMsg &		um,
			CpgDoc *					doc
			);


	/**
	*	@brief	close document
	*
	*	if \b the_doc is NULL current document is closed.
	*
	*	@returns true if the file was closed. Returns false if
	*	the file had unsaved changes and the user canceled the dialog or
	*	the file could not be saved
	*/
	static bool			 		closeDoc			(
			BBlocks::UserMsg &		um,
			CpgDoc *					the_doc = NULL
			);



	/**
	*	@brief	save the document
	*
	*	If \b the_doc is NULL current document is saved. If
	*	\b format is NULL default file format is used. If
	*	\b version is not provided default version is used.
	*
	*	If \b s_file is not provided the file pointed by \b the_doc
	*	is used. If it is a new document then the user is asked to select the
	*	location.
	*
	*	\b b_force_choose will ask for a file from the user even if a file
	*	is associated with the document.
	*
	*	\b b_update_data controld wheter we update the data that is
	*	stored in the document about the file and the "saved" status. Also,
	*	controls if the docSaved() signal is emited.
	*/
	static bool					saveDocument		(
			BBlocks::UserMsg &		um,
			CpgDoc *					the_doc = NULL,
			IDrawFile *				format = NULL,
			const BBlocks::Version3D &	version = BBlocks::Version3D(),
			const QString &			s_file = QString(),
			bool					b_force_choose = false,
			bool					b_update_data = true
			);


signals:


	/**
	*	@brief	a new document was loaded (either new or opened form file)
	*
	*	createNew() and openExisting() will tigger this signal. It will be
	*	followed by a crtDocChanged() signal.
	*/
	void						docEmerging			( CpgDoc * the_doc );


	/**
	*	@brief	a document was saved
	*
	*	saveDocument() will tigger this signal if allowed.
	*/
	void						docSaved			( CpgDoc * the_doc );


	/**
	*	@brief	a document was closed
	*
	*	saveDocument() will tigger this signal if allowed.
	*/
	void						docClosing			( CpgDoc * the_doc );



	/**
	*	@brief	emmited when current document changed
	*
	*	setCrtDoc() will tigger this signal.
	*/
	void						crtDocChanged		( CpgDoc * new_val );


	///@}
	/* ******************************************************************* */



	/* ******************************************************************* */
	/** @name 	Spaces related methods                                     */
	/* ******************************************************************* */
	///@{


public:


	/**
	*	@brief	get current space
	*
	*	For convenience. Uses crtDoc() to get current document and the it
	*	querries the instance for current space.
	*/
	static PgSpace *			crtSpace			( void );


	/**
	*	@brief	creates a new space in specified document
	*
	*	spaceEmerging() will be raised
	*/
	static PgSpace *			createSpace			(
			BBlocks::UserMsg &		um,
			CpgDoc *					doc = NULL,
			const QString &			s_name = QString()
			);


	/**
	*	@brief	creates a new space in specified document from a template space
	*
	*	If no space is provided the user is invited to select a file and a space.
	*	spaceEmerging() will be raised
	*/
	static PgSpace *			createSpaceFromTempl	(
			BBlocks::UserMsg &		um,
			CpgDoc *					doc = NULL,
			PgSpace *				s_tepl = NULL
			);


	/**
	*	@brief	removes the space indicated in argument
	*
	*	Will emit spaceDeleted()
	*/
	static bool					removeSpace		(
			BBlocks::UserMsg &		um,
			CpgDoc *					doc = NULL,
			PgSpace *				space = NULL
			);


	/**
	*	@brief	changes current space to the one povided as argument
	*
	*	Will emit crtSpaceChanged()
	*/
	static bool					setCrtSpace			(
			BBlocks::UserMsg &		um,
			CpgDoc *					doc,
			PgSpace *				the_space
			);


protected:


	/**
	*	@brief	To be used by PgSpace only
	*/
	void						emitSpaceEmerging	( PgSpace * the_space )
	{ emit spaceEmerging( the_space ); }


	/**
	*	@brief	To be used by PgSpace only
	*/
	void						emitSpaceDeleted	( PgSpace * the_space )
	{ emit spaceDeleted( the_space ); }


signals:


	/**
	*	@brief	a new space was created
	*/
	void						spaceEmerging		( PgSpace * the_space );


	/**
	*	@brief	a space was deleted
	*/
	void						spaceDeleted		( PgSpace * the_space );


	/**
	*	@brief	emmited when current space is changed
	*
	*	setCrtSpace() will tigger this signal.
	*/
	void						crtSpaceChanged		( PgSpace * new_crt );



	///@}
	/* ******************************************************************* */



	/* ******************************************************************* */
	/** @name 	Layers related methods                                     */
	/* ******************************************************************* */
	///@{


public:


	/**
	*	@brief	creates a new space in specified document
	*
	*	layerEmerging() will be raised
	*/
	static PgLayer *			createLayer			(
			BBlocks::UserMsg &		um,
			const QString &			s_name,
			CpgDoc *					doc,
			PgSpace *				space = NULL
			);


	/**
	*	@brief	removes specified layer
	*
	*	Will emit layerDeleted()
	*/
	static bool					removeLayer			(
			BBlocks::UserMsg &		um,
			PgSpace *				space,
			PgLayer *				layer
			);


	/**
	*	@brief	changes current layer to the one povided as argument
	*
	*	Will emit crtLayerChanged()
	*/
	static bool					setCrtLayer			(
			BBlocks::UserMsg &		um,
			PgSpace *				the_space,
			PgLayer *				the_layer
			);


protected:


	/**
	*	@brief	To be used by PgSpace only
	*/
	static void					emitLayerEmerging	(
			PgSpace * the_space, PgLayer * layer )
	{ emit uniq_->layerEmerging( the_space, layer ); }


	/**
	*	@brief	To be used by PgSpace only
	*/
	static void					emitLayerDeleted	(
			PgSpace * the_space, PgLayer * layer )
	{ emit uniq_->layerDeleted( the_space, layer ); }



signals:


	/**
	*	@brief	a new layer was created
	*/
	void						layerEmerging		(
			PgSpace * the_space,
			PgLayer * the_layer
			);


	/**
	*	@brief	a layer was deleted
	*/
	void						layerDeleted		(
			PgSpace * the_space,
			PgLayer * the_layer
			);


	/**
	*	@brief	emmited when current layer is changed
	*
	*	setCrtLayer() will tigger this signal.
	*/
	void						crtLayerChanged		(
			PgSpace *				the_space,
			PgLayer *				new_crt
			);


	///@}
	/* ******************************************************************* */







	/* ******************************************************************* */
	/** @name 	Utils                                                      */
	/* ******************************************************************* */
	///@{

public:


	static QString				lastPathComp				(
			const QString &			full_path
			);


	///@}
	/* ******************************************************************* */




public slots:



	/**
	*	@brief	close current document
	*
	*	@returns true if the file was closed. Returns false if
	*	the file had unsaved changes and the user canceled the dialog or
	*	the file could not be saved
	*/
	bool				close					( void );


	/**
	*	@brief	close all documents
	*
	*	@return true if there is no document left opened after the
	*	function returns. May return false because some
	*	file had unsaved changes and the user canceled the dialog or
	*	the file could not be saved.
	*/
	bool				closeAll				( void );


	/**
	*	@brief	copies the selection in current space
	*/
	void				copySel					( void );


	/**
	*	@brief	copies the selection in current space, then deletes it
	*/
	void				cutSel					( void );


	/**
	*	@brief	deletes the selection in current space
	*/
	void				deleteSel				( void );


	/**
	*	@brief	close the application
	*/
	void				exit					( void );


	/**
	*	@brief	create a new file
	*/
	void				newFile					( void );


	/**
	*	@brief	start a new instance
	*/
	void				newInstance				( void );


	/**
	*	@brief	open a file; the user is asked what file
	*/
	void				open					( void );


	/**
	*	@brief	paste the content of the clipboard in current space
	*/
	void				paste					( void );


	/**
	*	@brief	do next opperation in the stack for current space
	*/
	void				redo					( void );


	/**
	*	@brief	save current document
	*/
	void				save					( void );


	/**
	*	@brief	save current document with a different name
	*/
	void				saveAs					( void );


	/**
	*	@brief	undo previous opperation in the stack for current space
	*/
	void				undo					( void );


	/* item changes */
	/**
	*	@brief
	*/
	void				chgClone			( void );


	/**
	*	@brief
	*/
	void				chgExplode			( void );


	/**
	*	@brief
	*/
	void				chgMove			( void );


	/**
	*	@brief
	*/
	void				chgRotate			( void );


	/**
	*	@brief
	*/
	void				chgRotateScale			( void );


	/**
	*	@brief
	*/
	void				chgScale			( void );



	/* item create */
	/**
	*	@brief
	*/
	void				createBlock			( void );


	/**
	*	@brief
	*/
	void				createEllipse			( void );


	/**
	*	@brief
	*/
	void				createLine			( void );


	/**
	*	@brief
	*/
	void				createPolygone			( void );


	/**
	*	@brief
	*/
	void				createPolyline			( void );


	/**
	*	@brief
	*/
	void				createSpline			( void );


	/**
	*	@brief
	*/
	void				createText			( void );


	/* querries */
	/**
	*	@brief
	*/
	void				querryAngle			( void );


	/**
	*	@brief
	*/
	void				querryArea			( void );


	/**
	*	@brief
	*/
	void				querryDist			( void );


	/**
	*	@brief
	*/
	void				querryPoint			( void );


	/**
	*	@brief
	*/
	void				querrySlope			( void );


	/* various */


	/**
	*	@brief	Sets the maximum number of actions to store in undo/redo
	*	stack for any document
	*
	*	This limit is set for all opened documents and for all future documents.
	*
	*	For an comprehensive discussion about Undo/Redo functionality see
	*	\ref undo "dedicated page".
	*/
	void				setUndoLimit		( int new_lim );




signals:


	/**
	*	@brief	emmited when the app starts the closing steps
	*
	*	startClosing() will tigger this signal.
	*/
	void				closing				( void );


private:


	/**
	*	@brief	tries to get a proper temporary directory
	*/
	bool				getTempDir			( void );


	/*  FUNCTIONS    ======================================================= */
	//
	//
	//
	//

};	/*	class CpgLibrary	*/

}	/* namespace cpg */


bool			initCpgLibrary			( void );
void			endCpgLibrary			( void );


/*  CLASS    =============================================================== */
//
//
//
//
#endif // __CPGLIBRARY_INC__
/* ------------------------------------------------------------------------- */
/* ========================================================================= */
